package com.mlming.simpledb.backend.dm.pageCache;

import com.mlming.simpledb.backend.dm.page.Page;
import com.mlming.simpledb.backend.utils.Panic;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;

import com.mlming.simpledb.common.Error;

/**
 * 页面缓存
 */
public interface PageCache {
    // 这里参考大部分数据库的设计，将默认数据页大小定为 8K, 即: 2^13
    // 注意: 虽然Page对象里面很多参数, 但是并不是把Page对象存到磁盘文件里的, 而是把里面实际存储的数据也就是byte[] data进行存储的
    //      所以这里的数据页的定义, 其实是对 byte[] data的一个大小/长度 的定义
    public static final int PAGE_SIZE = 1 << 13;

    int newPage(byte[] initData); // 新建一个页
    Page getPage(int pgno) throws Exception; // 根据pageNumber获取一个页
    void release(Page page); // 在缓存中取消对该页的一个引用

    void truncateByBgno(int maxPgno); // 对DB文件从maxPgno对应的页之后的数据全部截断
    int getPageNumber(); // 获取当前的DB文件中存储的页面数量
    void flushPage(Page pg); // 把指定页刷盘,写回DB文件

    void close(); // 关闭页面缓存

    // 根据指定路径创建db文件, 且创建对应PageCacheImpl对象
    public static PageCacheImpl create(String path, long memory) {
        File f = new File(path+PageCacheImpl.DB_SUFFIX);
        try {
            if(!f.createNewFile()) {
                Panic.panic(Error.FileExistsException);
            }
        } catch (Exception e) {
            Panic.panic(e);
        }
        if(!f.canRead() || !f.canWrite()) {
            Panic.panic(Error.FileCannotRWException);
        }

        FileChannel fc = null;
        RandomAccessFile raf = null;
        try {
            raf = new RandomAccessFile(f, "rw");
            fc = raf.getChannel();
        } catch (FileNotFoundException e) {
            Panic.panic(e);
        }
        return new PageCacheImpl(raf, fc, (int)memory/PAGE_SIZE);
    }
    // 根据指定路径的已经存在的db文件, 创建对应PageCacheImpl对象
    public static PageCacheImpl open(String path, long memory) {
        File f = new File(path+PageCacheImpl.DB_SUFFIX);
        if(!f.exists()) {
            Panic.panic(Error.FileNotExistsException);
        }
        if(!f.canRead() || !f.canWrite()) {
            Panic.panic(Error.FileCannotRWException);
        }

        FileChannel fc = null;
        RandomAccessFile raf = null;
        try {
            raf = new RandomAccessFile(f, "rw");
            fc = raf.getChannel();
        } catch (FileNotFoundException e) {
            Panic.panic(e);
        }
        return new PageCacheImpl(raf, fc, (int)memory/PAGE_SIZE);
    }
}
